﻿#define _CRT_SECURE_NO_WARNINGS
#include "wifimanager.h"

/*utf-8转unicode*/
BOOL Utf8ToUnicode(std::wstring& str, const char* pUTF8Text)
{
    if (NULL == pUTF8Text)
    {
        return FALSE;
    }

    int  unicodeLen = ::MultiByteToWideChar(CP_UTF8, 0, pUTF8Text, -1, NULL, 0);

    wchar_t* pUnicode = NULL;

    try
    {
        pUnicode = new  wchar_t[unicodeLen + 1];
    }
    catch (...)
    {
        return FALSE;
    }

    MultiByteToWideChar(CP_UTF8, 0, pUTF8Text, -1, static_cast<LPWSTR>(pUnicode), unicodeLen);

    str = pUnicode;

    delete[]pUnicode;
    return TRUE;
}

//对比两个wstring字符串是否相同
bool compareNoCase(const std::wstring& str1, const std::wstring& str2) {
    if (str1.length() != str2.length()) {
        return false;
    }
    return std::equal(str1.begin(), str1.end(), str2.begin(), [](wchar_t a, wchar_t b) {
        return towlower(a) == towlower(b);
    });
}

/*获取所有网卡的GUID*/
bool WiFiManager::GetWlanInterfaces(InterfaceInfo_List& interfaceInfo_list)
{
    if (!InitialHandle())//初始化Wlanhandle
    {
        //std::cout << "initial wlan handle failed" << std::endl;
        return false;
    }
    //接受结果
    DWORD dwResult = 0;
    //网卡列表
    PWLAN_INTERFACE_INFO_LIST pIfList = NULL;
    //单独的网卡信息
    PWLAN_INTERFACE_INFO pIfInfo = NULL;
    //保存GUID
    WCHAR GuidString[39] = { 0 };
    // 释放内存
    unsigned int i = 0;
    //获取Wlan的网卡接口数据
    dwResult = WlanEnumInterfaces(m_wlanHandle, NULL, &pIfList);
    if (dwResult != ERROR_SUCCESS)
    {
        return false;
    }
    for (i = 0; i < (int)pIfList->dwNumberOfItems; i++) //遍历每个网卡
    {
        pIfInfo = (WLAN_INTERFACE_INFO*)&pIfList->InterfaceInfo[i];//获取网卡数组里面每个网卡信息
        //将GUID转换为字符串存储在GuidString里面
        //dwResult = StringFromGUID2(pIfInfo->InterfaceGuid, (LPOLESTR)&GuidString,
        //	sizeof(GuidString) / sizeof(*GuidString));
        interfaceInfo_list.push_back(pIfInfo->InterfaceGuid);
    }

    return true;
}

/*传入网卡的GUID，指定网卡扫描WiFi*/
bool WiFiManager::ScanWiFi(GUID& guid)
{
    if (!InitialHandle())//初始化Wlanhandle
    {
        //std::cout << "initial wlan handle failed" << std::endl;
        return false;
    }
    //接受结果
    DWORD dwResult = 0;
    // 向无线网卡发送探测请求，WlanScan探测会在4秒内陆续更新WIFIList
    dwResult = WlanScan(m_wlanHandle, &guid, NULL, NULL, NULL);
    if (dwResult != ERROR_SUCCESS)
    {
        return false;
    }
    return true;
}

/*获取WiFi连接*/
bool WiFiManager::GetWiFiList(WiFiInfoList& wifilist, GUID& guid)
{
    if (!InitialHandle()) {
        std::cout << "initial wlan handle failed" << std::endl;
        return false;
    }

    DWORD dwResult = 0;
    PWLAN_AVAILABLE_NETWORK_LIST pBssList = nullptr;
    PWLAN_AVAILABLE_NETWORK pBssEntry = nullptr;
    dwResult = WlanGetAvailableNetworkList(m_wlanHandle, &guid,2, NULL, &pBssList);
    if (dwResult != ERROR_SUCCESS) {
        std::cout << "WlanGetAvailableNetworkList failed with error: " << dwResult << std::endl;
        return false;
    }
    for (unsigned int j = 0; j < pBssList->dwNumberOfItems; ++j) {
        pBssEntry = &pBssList->Network[j];
        WiFiInfo info;
        // 获取 SSID
        char ssid[36];
        //判断获取SSID长度是不是大于0，若小于0，则归为隐藏网络
        if (pBssEntry->dot11Ssid.uSSIDLength > 0)
        {
            memcpy(ssid, pBssEntry->dot11Ssid.ucSSID, pBssEntry->dot11Ssid.uSSIDLength);
            ssid[pBssEntry->dot11Ssid.uSSIDLength] = 0;
            if (!Utf8ToUnicode(info.cstrSSID, ssid)) {
                continue;
            }
        }
        else {
            std::wstring Invisible_network = L"隐藏的网络";
            info.cstrSSID = Invisible_network;
        }
        // 信号强度
        info.nSignalValue = pBssEntry->wlanSignalQuality;

        // 连接状态
        info.bLinked = (pBssEntry->dwFlags & WLAN_AVAILABLE_NETWORK_CONNECTED) != 0;

        //是否启用加密,也就是是否需要密码进行连接
        info.is_encrypt = pBssEntry->bSecurityEnabled;

        //加密方式
        info.dot11BssType = pBssEntry->dot11BssType;

        //指示用于首次加入此网络的默认身份验证算法
        info.dot11DefaultAuthAlgorithm = pBssEntry->dot11DefaultAuthAlgorithm;

        //指示加入此网络时要使用的默认密码算法
        info.dot11DefaultCipherAlgorithm = pBssEntry->dot11DefaultCipherAlgorithm;
        // 过滤名称相同的 SSID
        bool bRepeat = false;
        for (auto& existingInfo : wifilist) {
            if (compareNoCase(existingInfo.cstrSSID, info.cstrSSID)) {
                existingInfo.bLinked |= info.bLinked;
                bRepeat = true;
                break;
            }
        }
        if (bRepeat) {
            continue;
        }
        DWORD dwFlags = WLAN_PROFILE_GET_PLAINTEXT_KEY|WLAN_PROFILE_USER;//设置文件
        const size_t bufferSize = 4096;
        std::vector<wchar_t> profileBuffer(bufferSize);
        DWORD dwGrantedAccess = WLAN_READ_ACCESS;
        //尝试获取此ssid在系统里面的配置文件
        dwResult = WlanGetProfile(m_wlanHandle, &guid, info.cstrSSID.c_str(), NULL, &info.pProfileXml,  &dwFlags, &dwGrantedAccess);
        if (dwResult == ERROR_SUCCESS) {
            // 获取密码
            std::wstring wstrXml = info.pProfileXml;
            size_t nFirstIndex = wstrXml.find(L"<keyMaterial>");
            if (nFirstIndex != std::wstring::npos) {
                size_t nLastIndex = wstrXml.find(L"</keyMaterial>");
                std::wstring strKey = wstrXml.substr(nFirstIndex + 13, nLastIndex - (nFirstIndex + 13));
                BYTE byteKey[1024] = { 0 };
                DWORD dwLength = 1024;
                DATA_BLOB dataOut, dataVerify;
                //ImpersonateLoginUser();//获取local system权限
                BOOL bRes = CryptStringToBinaryW(strKey.c_str(), static_cast<DWORD>(strKey.length()), CRYPT_STRING_HEXRAW, byteKey, &dwLength, 0, 0);
                if (bRes) {
                    dataOut.cbData = dwLength;
                    dataOut.pbData = byteKey;
                    LPWSTR pDescrOut = NULL;
                    //解密密码
                    if (CryptUnprotectData(&dataOut, NULL, NULL, NULL, NULL, 0, &dataVerify)) {
                        //这个地方转换有点问题,可能获取的密码不对
                        std::wstring decodedKey(reinterpret_cast<wchar_t*>(dataVerify.pbData), dataVerify.cbData / sizeof(wchar_t));
                        info.cstrPassword = decodedKey;
                        LocalFree(dataVerify.pbData);
                    }
                    else {
                        //解密失败，输出错误原因，大概率是权限不够
                        DWORD errorCode = GetLastError();
                        LPWSTR errorMessage = nullptr;
                        FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                                      nullptr, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&errorMessage, 0, nullptr);
                        if (errorMessage) {
                            std::wcout << L"CryptUnprotectData failed with error code " << errorCode << L": " << errorMessage << std::endl;
                            LocalFree(errorMessage);
                        }
                    }
                    RevertToSelf();
                }
            }
            //获取connectionMode模式,是手动还是自动
            size_t nFirstIndex_mode = wstrXml.find(L"<connectionMode>");
            if (nFirstIndex != std::wstring::npos) {
                size_t nLastIndex_mode = wstrXml.find(L"</connectionMode>");
                std::wstring connectionMode = wstrXml.substr(nFirstIndex_mode + 16, nLastIndex_mode - (nFirstIndex_mode + 16));
                if (connectionMode == L"manual") {
                    info.connectionMode = true;
                }
                else {
                    info.connectionMode = false;
                }
            }
        }
        else if (dwResult == ERROR_NOT_FOUND) {
            // 说明这个 ssid 之前没有连接过
        }
        // 每一个 wifi 放到 wifilist
        wifilist.emplace_back(info);
    }

    return true;

}

/*连接WiFi*/
bool WiFiManager::ConnectWiFi(WiFiInfo wifiinfo, std::wstring targetKey,GUID guid, bool is_manual)
{
    if (!InitialHandle()) {
        std::cout << "initial wlan handle failed" << std::endl;
        return false;
    }

    DWORD dwResult = 0;

    //重新将用户的密码重新配置到文件
    SetProfile(wifiinfo, targetKey, guid,is_manual);

    //指定使用 WlanConnect 函数时使用的参数。
    WLAN_CONNECTION_PARAMETERS wlanConnPara;
    /*
    wlan_connection_mode_profile = 0, 配置文件将用于建立连接。
    wlan_connection_mode_temporary_profile,临时配置文件将用于建立连接。下次不会自动连接
    wlan_connection_mode_discovery_secure,安全发现将用于建立连接。
    wlan_connection_mode_discovery_unsecure,不安全的发现将用于建立连接。
    wlan_connection_mode_auto,连接由无线服务使用永久性配置文件自动启动。
    wlan_connection_mode_invalid,未使用。
    */
    wlanConnPara.wlanConnectionMode = wlan_connection_mode_profile;
    wlanConnPara.strProfile = wifiinfo.cstrSSID.c_str();
    wlanConnPara.pDot11Ssid = nullptr;
    wlanConnPara.dot11BssType = dot11_BSS_type_infrastructure;
    wlanConnPara.pDesiredBssidList = nullptr;
    wlanConnPara.dwFlags = WLAN_CONNECTION_HIDDEN_NETWORK;
    dwResult = WlanConnect(m_wlanHandle,&guid, &wlanConnPara, NULL);
    if (dwResult == ERROR_SUCCESS) {
        // WlanConnect是立即返回的，所有返回值没意义，通过注册的回调函数OnNotificationCallback获取连接结果
    }
    return true;
}

/*断开WiFi*/
bool WiFiManager::DisconnectWiFi(GUID guid)
{
    if (!InitialHandle()) {
        std::cout << "initial wlan handle failed" << std::endl;
        return false;
    }
    DWORD dwResult = 0;
    dwResult = WlanDisconnect(m_wlanHandle, &guid,NULL);
    if (dwResult == ERROR_SUCCESS) {

    }
    return true;
}

/*关闭WiFi*/
bool WiFiManager::Set_WiFi_Status(bool status,GUID guid) {
    if (!InitialHandle()) {
        std::cout << "initial wlan handle failed" << std::endl;
        return false;
    }
    DWORD dwResult = 0;
    WLAN_PHY_RADIO_STATE state;
    state.dwPhyIndex = 0;
    if (status) {
        state.dot11SoftwareRadioState = dot11_radio_state_on;
    }
    else {
        state.dot11SoftwareRadioState = dot11_radio_state_off;
    }
    PVOID pData = &state;
    dwResult = WlanSetInterface(m_wlanHandle, &guid,wlan_intf_opcode_radio_state, sizeof(WLAN_PHY_RADIO_STATE), pData, NULL);
    if (dwResult != ERROR_SUCCESS)
    {
        return false;
    }
    return true;
}

/*设置用户权限*/
bool WiFiManager::ImpersonateLoginUser() {
    // HANDLE hToken, hProcess;
    // DWORD  dwId = GetProcessID(L"winlogon.exe");
    // if (dwId) {
    //     if (SetPrivilege(SE_DEBUG_NAME)) {
    //         hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwId);
    //         if (hProcess != NULL) {
    //             if (OpenProcessToken(hProcess, TOKEN_IMPERSONATE | TOKEN_DUPLICATE | TOKEN_QUERY, &hToken)) {
    //                 if (ImpersonateLoggedOnUser(hToken)) {
    //                     return true;
    //                 }
    //                 CloseHandle(hToken);
    //             }
    //             CloseHandle(hProcess);
    //         }
    //     }
    // }
    // return false;
    return false;
}

/*注册回调函数*/
bool WiFiManager::RigisterConnectNotification(CONNECT_NOTIFICATION_CALLBACK funcCallback)
{
    if (funcCallback) {
        m_funcConnectNotification = funcCallback;
    }
    return false;
}

/*回调函数，当WiFi有事件发生的时候触发*/
void OnNotificationCallback(PWLAN_NOTIFICATION_DATA data, PVOID context)
{
    if (data != NULL && data->NotificationSource == WLAN_NOTIFICATION_SOURCE_ACM)
    {
        switch (data->NotificationCode)
        {
        case wlan_notification_acm_scan_complete:
        case wlan_notification_acm_scan_fail:
            WiFiManager::GetInstance().SetConnectResult(NULL, data->NotificationCode);
            break;
        case wlan_notification_acm_connection_start:
        case wlan_notification_acm_connection_complete:
        case wlan_notification_acm_connection_attempt_fail:
        case wlan_notification_acm_disconnecting:
        case wlan_notification_acm_disconnected:
        {
            PWLAN_CONNECTION_NOTIFICATION_DATA connection = (PWLAN_CONNECTION_NOTIFICATION_DATA)data->pData;
            WiFiManager::GetInstance().SetConnectResult(connection, data->NotificationCode);
        }
        case wlan_notification_acm_profile_change:
        {
            //std::cout << "配置文件改变" << std::endl;
        }
        break;
        default:
            break;
        }
    }
}

/*初始化WiFi句柄*/
bool WiFiManager::InitialHandle()
{
    DWORD dwResult = 0;
    DWORD dwCurVersion = 0;
    DWORD dwMaxClient = 2;
    if (m_wlanHandle == NULL)
    {
        if ((dwResult = WlanOpenHandle(dwMaxClient, NULL, &dwCurVersion, &m_wlanHandle)) != ERROR_SUCCESS)
        {
            std::cout << "wlanOpenHandle failed with error: " << dwResult << std::endl;
            m_wlanHandle = NULL;
            return false;
        }

        // 注册消息通知回调
        if ((dwResult = WlanRegisterNotification(m_wlanHandle, WLAN_NOTIFICATION_SOURCE_ALL, TRUE, WLAN_NOTIFICATION_CALLBACK(OnNotificationCallback), NULL, nullptr, nullptr)) != ERROR_SUCCESS)
        {
            std::cout << "wlanRegisterNotification failed with error: " << dwResult << std::endl;
            m_wlanHandle = NULL;
            return false;
        }
    }
    return true;
}

/*设置用户文件,标准Profile格式请上MSDN查找*/
bool WiFiManager::SetProfile(WiFiInfo& wifiinfo, const std::wstring& targetKey,  const GUID& interfaceGuid,bool is_manual) {
    std::wstring szProfileXML;  // Profile XML流

    // 组合参数XML码流
    // 头
    szProfileXML += L"<?xml version=\"1.0\"?><WLANProfile xmlns=\"http://www.microsoft.com/networking/WLAN/profile/v1\"><name>";
    // name，一般与SSID相同
    szProfileXML += wifiinfo.cstrSSID;
    // SSIDConfig
    szProfileXML += L"</name><SSIDConfig><SSID><name>";
    szProfileXML += wifiinfo.cstrSSID;
    szProfileXML += L"</name></SSID></SSIDConfig>";
    // connectionType
    szProfileXML += L"<connectionType>";
    switch (wifiinfo.dot11BssType) {
    case dot11_BSS_type_infrastructure:
        szProfileXML += L"ESS";
        //std::wcout << L"ESS connection type" << std::endl;
        break;
    case dot11_BSS_type_independent:
        szProfileXML += L"IBSS";
        //std::wcout << L"IBSS connection type" << std::endl;
        break;
    case dot11_BSS_type_any:
        szProfileXML += L"Any";
        //std::wcout << L"Any connection type" << std::endl;
        break;
    default:
        //std::wcout << L"Unknown BSS type" << std::endl;
        return false;
    }

    szProfileXML += L"</connectionType>";
    if (is_manual) {
        //connectionMode
        szProfileXML += L"<connectionMode>manual</connectionMode>";
    }
    else
    {
        //connectionMode
        szProfileXML += L"<connectionMode>auto</connectionMode>";
    }

    szProfileXML += L"<MSM><security><authEncryption><authentication>";
    switch (wifiinfo.dot11DefaultAuthAlgorithm) {
    case DOT11_AUTH_ALGO_80211_OPEN:
        szProfileXML += L"open";
        //std::wcout << L"Open 802.11 authentication" << std::endl;
        break;
    case DOT11_AUTH_ALGO_80211_SHARED_KEY:
        szProfileXML += L"shared";
        //std::wcout << L"Shared 802.11 authentication" << std::endl;
        break;
    case DOT11_AUTH_ALGO_WPA:
        szProfileXML += L"WPA";
        //std::wcout << L"WPA-Enterprise 802.11 authentication" << std::endl;
        break;
    case DOT11_AUTH_ALGO_WPA_PSK:
        szProfileXML += L"WPAPSK";
        //std::wcout << L"WPA-Personal 802.11 authentication" << std::endl;
        break;
    case DOT11_AUTH_ALGO_WPA_NONE:
        szProfileXML += L"none";
        //std::wcout << L"WPA-NONE, not exist in MSDN" << std::endl;
        break;
    case DOT11_AUTH_ALGO_RSNA:
        szProfileXML += L"WPA2";
        //std::wcout << L"WPA2-Enterprise 802.11 authentication" << std::endl;
        break;
    case DOT11_AUTH_ALGO_RSNA_PSK:
        szProfileXML += L"WPA2PSK";
        //std::wcout << L"WPA2-Personal 802.11 authentication" << std::endl;
        break;
    default:
        //std::wcout << L"Unknown authentication" << std::endl;
        return false;
    }
    szProfileXML += L"</authentication><encryption>";
    switch (wifiinfo.dot11DefaultCipherAlgorithm) {
    case DOT11_CIPHER_ALGO_NONE:
        szProfileXML += L"none";
        break;
    case DOT11_CIPHER_ALGO_WEP40:
        szProfileXML += L"WEP";
        break;
    case DOT11_CIPHER_ALGO_TKIP:
        szProfileXML += L"TKIP";
        break;
    case DOT11_CIPHER_ALGO_CCMP:
        szProfileXML += L"AES";
        break;
    case DOT11_CIPHER_ALGO_WEP104:
        szProfileXML += L"WEP";
        break;
    case DOT11_CIPHER_ALGO_WEP:
        szProfileXML += L"WEP";
        break;
    case DOT11_CIPHER_ALGO_WPA_USE_GROUP:
        std::wcout << L"GCMP256" << std::endl;
    default:
        std::wcout << L"Unknown encryption" << std::endl;
        return false;
    }
    szProfileXML += L"</encryption></authEncryption><sharedKey><keyType>passPhrase</keyType><protected>false</protected><keyMaterial>";
    szProfileXML += targetKey;
    // 尾
    szProfileXML += L"</keyMaterial></sharedKey></security></MSM></WLANProfile>";

    // 设置网络参数
    DWORD dwReasonCode = 0;
    DWORD dwResult = WlanSetProfile(m_wlanHandle, &interfaceGuid,
                                    0x00, szProfileXML.c_str(), NULL, TRUE, NULL, &dwReasonCode);
    if (dwResult != ERROR_SUCCESS) {
        switch (dwResult) {
        case ERROR_INVALID_PARAMETER:
            std::wcerr << L"Para is NULL" << std::endl;
            break;
        case ERROR_INVALID_HANDLE:
            std::wcerr << L"Failed to INVALID HANDLE" << std::endl;
            break;
        case ERROR_NOT_ENOUGH_MEMORY:
            std::wcerr << L"Failed to allocate memory" << std::endl;
            break;
        case ERROR_BAD_PROFILE:
            std::wcerr << L"The profile specified by strProfileXml is not valid" << std::endl;
            break;
        case ERROR_ALREADY_EXISTS:
            std::wcerr << L"strProfileXml specifies a network that already exists" << std::endl;
            break;
        case ERROR_ACCESS_DENIED:
            std::wcerr << L"The caller does not set the profile." << std::endl;
            break;
        default:
            dwResult = GetLastError();
            std::wcerr << L"WlanSetProfile Fail： " << dwResult << std::endl;
            break;
        }
        if (dwResult != ERROR_ALREADY_EXISTS) {
            return false;
        }
    }
    return true;
}

/*设置连接结果*/
bool WiFiManager::SetConnectResult(PWLAN_CONNECTION_NOTIFICATION_DATA pData, DWORD dwConnectCode)
{
    if (m_funcConnectNotification)
    {
        m_funcConnectNotification(pData, dwConnectCode);
    }
    return false;
}

/*获取进程ID*/
DWORD WiFiManager::GetProcessID(const wchar_t* szName) {
    // DWORD dwId = 0;
    // HANDLE hSnap;
    // BOOL bResult;
    // PROCESSENTRY32 pe32;
    // pe32.dwSize = sizeof(PROCESSENTRY32);
    // hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    // if (hSnap != INVALID_HANDLE_VALUE) {
    //     bResult = Process32First(hSnap, &pe32);
    //     while (bResult) {
    //         if (lstrcmpi(pe32.szExeFile, szName) == 0) {
    //             dwId = pe32.th32ProcessID;
    //             break;
    //         }
    //         bResult = Process32Next(hSnap, &pe32);
    //     }
    //     CloseHandle(hSnap);
    // }
    // return dwId;
    return false;
}

/*设置权限*/
bool WiFiManager::SetPrivilege(const wchar_t* szPrivilege) {
    // HANDLE hToken;
    // BOOL bResult;
    // LUID luid;
    // TOKEN_PRIVILEGES tp;
    // bResult = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken);
    // if (bResult) {
    //     bResult = LookupPrivilegeValue(NULL, szPrivilege, &luid);
    //     if (bResult) {
    //         tp.PrivilegeCount = 1;
    //         tp.Privileges[0].Luid = luid;
    //         tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    //         bResult = AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);
    //     }
    //     CloseHandle(hToken);
    // }
    // return bResult;
    return false;
}

